

public class MiniMax {

	int cols, rows, playerID, maxNoOfCoins;

	public MiniMax(int cols, int rows, int playerID, int maxNoOfCoins) {
		this.cols = cols;
		this.rows = rows;
		this.playerID = playerID;
		this.maxNoOfCoins = maxNoOfCoins;
	}
	
	public int[] alphabeta(State state, int depth, int[] alpha, int[] beta, boolean player) {
		ReturnValue terminalTest = terminalTest(state);
		if(depth == 0 || !terminalTest.winner.equals(IGameLogic.Winner.NOT_FINISHED)) {
			return utility(terminalTest);
		}
				
		// If Max
		if(player) {
			for (int i = 0; i < state.coinsPerCol.length; i++) {
				if (state.coinsPerCol[i] < rows) {
					State tmpState = new State(state);
					tmpState.insertCoin(i, playerID);

					int[] tmpAlpha = new int[2];
					System.arraycopy(alpha, 0, tmpAlpha, 0, 2);
					int[] tmpBeta = new int[2];
					System.arraycopy(beta, 0, tmpBeta, 0, 2);
					int tmpV = alphabeta(tmpState, depth-1, tmpAlpha, tmpBeta, !player)[0];
					if (tmpV > alpha[0]) {
						alpha[0] = tmpV;
						alpha[1]= i;
					}
					if(alpha[0] >= beta[0])
						break;
				}
			}
			return alpha;
		} else {
			for (int i = 0; i < state.coinsPerCol.length; i++) {
				if (state.coinsPerCol[i] < rows) {
					State tmpState = new State(state);
					if (playerID == 1)
						tmpState.insertCoin(i, 2);
					else
						tmpState.insertCoin(i, 1);

					int[] tmpAlpha = new int[2];
					System.arraycopy(alpha, 0, tmpAlpha, 0, 2);
					int[] tmpBeta = new int[2];
					System.arraycopy(beta, 0, tmpBeta, 0, 2);
					
					int tmpV = alphabeta(tmpState, depth-1, tmpAlpha, tmpBeta, !player)[0];
					if (tmpV < beta[0]) {
						beta[0] = tmpV;
						beta[1]= i;
					}
					if(alpha[0] >= beta[0])
						break;
				}
			}
			return beta;
		}

	}

	private int[] utility(ReturnValue value)
	{
		if (playerID == 1 && value.winner.equals(IGameLogic.Winner.PLAYER1))
			return new int[] {Integer.MAX_VALUE-1, -1};
		else if (playerID == 2 && value.winner.equals(IGameLogic.Winner.PLAYER2))
			return new int[] {Integer.MAX_VALUE-1, -1};
		else if (value.winner.equals(IGameLogic.Winner.TIE))
			return new int[] {0, -1};
		else if (value.winner.equals(IGameLogic.Winner.NOT_FINISHED))
			return new int[] {value.value, -1};
		else
			return new int[] {Integer.MIN_VALUE+1, -1};
	}

	public ReturnValue terminalTest(State state) {
		ReturnValue val = new ReturnValue();
		
		// Horizontal
		int hor = 1;

		for(int i = state.lastCoinCol+1; i < state.cols; i++) {
			if(state.get(i, state.lastCoinRow) != state.lastPlayerId) {
				break;
			}

			hor++;
		}

		for(int j = state.lastCoinCol-1; j >= 0; j--) {
			if(state.get(j, state.lastCoinRow) != state.lastPlayerId) {
				break;
			}

			hor++;
		}

		if(hor >= 4) {
			// Winner
			if(state.lastPlayerId == 1) {
				val.winner = IGameLogic.Winner.PLAYER1;
			} else {
				val.winner = IGameLogic.Winner.PLAYER2;
			}
			return val;
		}

		// Vertical
		int vert = 1;

		for(int i = state.lastCoinRow+1; i < state.rows; i++) {
			if(state.get(state.lastCoinCol, i) != state.lastPlayerId) {
				break;
			}

			vert++;
		}

		for(int j = state.lastCoinRow-1; j >= 0; j--) {
			if(state.get(state.lastCoinCol, j) != state.lastPlayerId) {
				break;
			}

			vert++;
		}

		if(vert >= 4) {
			// Winner
			if(state.lastPlayerId == 1) {
				val.winner = IGameLogic.Winner.PLAYER1;
			} else {
				val.winner = IGameLogic.Winner.PLAYER2;
			}
			return val;
		}

		// Diagonal 1
		int diag1 = 1;

		for(int i = state.lastCoinCol+1, j = state.lastCoinRow+1; i < state.cols && j < state.rows; i++, j++) {
			if(state.get(i, j) != state.lastPlayerId) {
				break;
			}

			diag1++;
		}

		for(int i = state.lastCoinCol-1, j = state.lastCoinRow-1; i >= 0 && j >= 0; i--, j--) {
			if(state.get(i, j) != state.lastPlayerId) {
				break;
			}

			diag1++;
		}

		if(diag1 >= 4) {
			// Winner
			if(state.lastPlayerId == 1) {
				val.winner = IGameLogic.Winner.PLAYER1;
			} else {
				val.winner = IGameLogic.Winner.PLAYER2;
			}
			return val;
		}

		// Diagonal 2
		int diag2 = 1;

		for(int i = state.lastCoinCol+1, j = state.lastCoinRow-1; i < state.cols && j >= 0; i++, j--) {
			if(state.get(i, j) != state.lastPlayerId) {
				break;
			}

			diag2++;
		}

		for(int i = state.lastCoinCol-1, j = state.lastCoinRow+1; i >= 0 && j < state.rows; i--, j++) {
			if(state.get(i, j) != state.lastPlayerId) {
				break;
			}

			diag2++;
		}

		if(diag2 >= 4) {
			// Winner
			if(state.lastPlayerId == 1) {
				val.winner = IGameLogic.Winner.PLAYER1;
			} else {
				val.winner = IGameLogic.Winner.PLAYER2;
			}
			return val;
		}

		// Tie
		if (state.placedCoins >= maxNoOfCoins)
		{
			val.winner = IGameLogic.Winner.TIE;
			return val;
		}
		
		val.winner = IGameLogic.Winner.NOT_FINISHED;
		
		if (state.lastPlayerId == playerID)
			val.value = hor+vert+diag1+diag2;
		else
			val.value = -hor-vert-diag1-diag2;
		
		return val;
	}
}
